/**
 * @author re0hg
 * @version 1.0
 * @date 2025/3/12
 */
import jssc.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SerialCommunicationApp extends JFrame {
    private JButton openWindowButton;
    private static final String[] baudRates = { "9600", "19200", "38400", "57600", "115200" };
    private static final String[] dataBits = { "8", "7","6","5" };
    private static final String[] stopBits = { "1", "1.5", "2" };
    private static final String[] parities = { "None", "Even", "Odd" };

    public SerialCommunicationApp() {
        setTitle("串口通信管理");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        openWindowButton = new JButton("打开新串口窗口");
        openWindowButton.addActionListener(e -> new SerialPortWindow().setVisible(true));

        JPanel panel = new JPanel();
        panel.add(openWindowButton);
        getContentPane().add(panel, BorderLayout.CENTER);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new SerialCommunicationApp().setVisible(true));
    }

    public class SerialPortWindow extends JFrame {
        private JComboBox<String> portComboBox;
        private JTextPane logTextPane;
        private StyledDocument logDoc;
        private JComboBox<String> baudRateComboBox, dataBitsComboBox, stopBitsComboBox, parityComboBox;
        private JButton connectButton, sendButton, disconnectButton;
        private JTextField sendTextField;
        private SerialPort serialPort;
        private StringBuilder receiveBuffer = new StringBuilder(); // 接收缓冲区

        public SerialPortWindow() {
            setTitle("串口通信");
            setSize(600, 400);
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setLocationRelativeTo(null);

            // 初始化组件
            portComboBox = new JComboBox<>(getAvailablePorts());
            baudRateComboBox = new JComboBox<>(baudRates);
            dataBitsComboBox = new JComboBox<>(dataBits);
            stopBitsComboBox = new JComboBox<>(stopBits);
            parityComboBox = new JComboBox<>(parities);

            logTextPane = new JTextPane();
            logTextPane.setEditable(false);
            logDoc = logTextPane.getStyledDocument();
            JScrollPane scrollPane = new JScrollPane(logTextPane);

            sendTextField = new JTextField();
            sendButton = new JButton("发送");
            connectButton = new JButton("连接");
            disconnectButton = new JButton("取消连接");

            // 默认取消连接按钮不可见
            disconnectButton.setVisible(false);

            // 布局设置
            JPanel panel = new JPanel(new GridLayout(6, 2));
            panel.add(new JLabel("选择串口")); panel.add(portComboBox);
            panel.add(new JLabel("波特率")); panel.add(baudRateComboBox);
            panel.add(new JLabel("数据位")); panel.add(dataBitsComboBox);
            panel.add(new JLabel("停止位")); panel.add(stopBitsComboBox);
            panel.add(new JLabel("奇偶校验")); panel.add(parityComboBox);
            panel.add(connectButton);
            panel.add(disconnectButton); // 添加取消连接按钮

            JPanel sendPanel = new JPanel(new BorderLayout());
            sendPanel.add(sendTextField, BorderLayout.CENTER);
            sendPanel.add(sendButton, BorderLayout.EAST);

            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(panel, BorderLayout.NORTH);
            getContentPane().add(scrollPane, BorderLayout.CENTER);
            getContentPane().add(sendPanel, BorderLayout.SOUTH);

            // 事件监听
            connectButton.addActionListener(e -> connectSerialPort());
            disconnectButton.addActionListener(e -> disconnectSerialPort());
            sendButton.addActionListener(e -> sendData());
        }

        private void connectSerialPort() {
            try {
                String portName = (String) portComboBox.getSelectedItem();
                int baudRate = Integer.parseInt((String) baudRateComboBox.getSelectedItem());
                int dataBits = Integer.parseInt((String) dataBitsComboBox.getSelectedItem());
                int stopBits = Integer.parseInt((String) stopBitsComboBox.getSelectedItem());
                int parity = getParitySetting((String) parityComboBox.getSelectedItem());

                serialPort = new SerialPort(portName);
                serialPort.openPort();
                serialPort.setParams(baudRate, dataBits, stopBits, parity);

                new Thread(() -> {
                    try {
                        while (serialPort.isOpened()) {
                            byte[] buffer = serialPort.readBytes();
                            if (buffer != null && buffer.length > 0) {
                                String receivedPart = new String(buffer, "UTF-8");
                                receiveBuffer.append(receivedPart);

                                // 按换行符分割完整消息
                                int endIndex;
                                while ((endIndex = receiveBuffer.indexOf("\n")) != -1) {
                                    String fullMessage = receiveBuffer.substring(0, endIndex);
                                    receiveBuffer.delete(0, endIndex + 1);
                                    appendToLog("[RECV] " + getCurrentTimestamp() + " " + fullMessage, Color.BLUE);
                                }
                            }
                        }
                    } catch (SerialPortException | UnsupportedEncodingException e) {
                        appendToLog("接收错误: " + e.getMessage(), Color.RED);
                    }
                }).start();

                appendToLog("连接成功", Color.BLACK);
                connectButton.setVisible(false); // 隐藏连接按钮
                disconnectButton.setVisible(true); // 显示取消连接按钮

            } catch (SerialPortException e) {
                appendToLog("连接失败: " + e.getMessage(), Color.RED);
            }
        }

        private void disconnectSerialPort() {
            try {
                if (serialPort != null && serialPort.isOpened()) {
                    serialPort.closePort();
                    appendToLog("断开连接", Color.RED);
                    connectButton.setVisible(true); // 显示连接按钮
                    disconnectButton.setVisible(false); // 隐藏取消连接按钮
                }
            } catch (SerialPortException e) {
                appendToLog("断开连接失败: " + e.getMessage(), Color.RED);
            }
        }

        private void sendData() {
            if (serialPort == null || !serialPort.isOpened()) {
                appendToLog("串口未连接", Color.RED);
                return;
            }

            String data = sendTextField.getText();
            String timestamp = getCurrentTimestamp();
            String fullMessage = "[SENT] " + timestamp + " " + data + "\n"; // 添加换行符作为结束符

            try {
                byte[] bytesToSend = fullMessage.getBytes("UTF-8");
                serialPort.writeBytes(bytesToSend);
                appendToLog(fullMessage.trim(), Color.BLACK); // 发送日志（去除换行符）
            } catch (SerialPortException | UnsupportedEncodingException e) {
                appendToLog("发送失败: " + e.getMessage(), Color.RED);
            }
        }

        // 核心改进：每条日志独立设置颜色
        private void appendToLog(String message, Color color) {
            SwingUtilities.invokeLater(() -> {
                try {
                    SimpleAttributeSet attrs = new SimpleAttributeSet();
                    StyleConstants.setForeground(attrs, color);
                    logDoc.insertString(logDoc.getLength(), message + "\n", attrs);
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
            });
        }

        // 辅助方法
        private String getCurrentTimestamp() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        }

        private String[] getAvailablePorts() {
            String[] ports = SerialPortList.getPortNames();
            return ports.length == 0 ? new String[]{"无可用串口"} : ports;
        }

        private int getParitySetting(String parity) {
            switch (parity) {
                case "Even": return SerialPort.PARITY_EVEN;
                case "Odd": return SerialPort.PARITY_ODD;
                default: return SerialPort.PARITY_NONE;
            }
        }
    }

}
